home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / inn1.000 / inn1.4sec-linux-src.tar / inn / expire / fastrm.c < prev    next >
C/C++ Source or Header  |  1993-03-18  |  15KB  |  730 lines

  1. /*
  2.  * delete a list of filenames from stdin
  3.  *
  4.  * exit(0) if all is OK (files that can't be unlinked because they
  5.  *    didn't exist is "OK")
  6.  *
  7.  * exit(1) in other cases - problems with stdin, no permission, ...
  8.  * written by <kre@munnari.oz.au>
  9.  */
  10. #include <stdio.h>
  11. #include <ctype.h>
  12. #include <sys/types.h>
  13. #include <sys/stat.h>
  14. #include <errno.h>
  15. #include "configdata.h"
  16. #include "mydir.h"
  17. #include "clibrary.h"
  18. #include "macros.h"
  19. #include "libinn.h"
  20.  
  21. #define MAX_LINE_SIZE    1024
  22. #define SHORT_NAME    16
  23. #define MAX_DIR_LEN    2048
  24.  
  25. /* typedef unsigned char chr; */
  26. typedef char    chr;
  27.  
  28. typedef struct _dnode {
  29.     struct _dnode    *next;
  30.     int            count;
  31.     chr            *dir;
  32.     chr            *longname;
  33.     chr            shortname[SHORT_NAME];
  34. } dnode;
  35.  
  36. #define    NODENAME(d)    ((d)->longname ? (d)->longname : (d)->shortname)
  37.  
  38.  
  39. STATIC char    DotDot[] = "../../../../";
  40. STATIC chr    base_dir[MAX_DIR_LEN];
  41. STATIC chr    cur_dir[MAX_DIR_LEN];
  42. STATIC chr    prefix_dir[MAX_DIR_LEN];
  43. STATIC int    prefix_len;
  44. STATIC char    *MyName;
  45. STATIC int    fatals;                /* error counter */
  46. STATIC BOOL    AmRoot;
  47. STATIC BOOL    Debugging;
  48. STATIC int    dotdot;
  49. STATIC int    sortdirs;
  50. STATIC int    cdval = 3;
  51.  
  52.  
  53. STATIC void
  54. err_exit(s)
  55.     char    *s;
  56. {
  57.     (void)fprintf(stderr, "%s: %s\n", MyName, s);
  58.     exit(1);
  59. }
  60.  
  61.  
  62. STATIC int
  63. myexit()
  64. {
  65.     err_exit("Could not allocate memory");
  66.     /* NOTREACHED */
  67. }
  68.  
  69.  
  70. /*
  71. **  Get the next line from stdin into 'l' which has 'len' bytes available.
  72. */
  73. STATIC BOOL
  74. get_line(l, len)
  75.     register chr    *l;
  76.     register int    len;
  77. {
  78.     static int        count;
  79.     register chr    *p;
  80.  
  81.     for ( ; ; ) {
  82.     /* Get the line. */
  83.     if (fgets(l, len, stdin) == NULL)
  84.         return FALSE;
  85.     count++;
  86.  
  87.     /* See if we got the \n terminator. */
  88.     p = (chr *)strchr(l, '\n');
  89.     if (p != NULL) {
  90.         /* Yes, ok, that's a good line, trash the \n & return. */
  91.         *p = '\0';
  92.         return TRUE;
  93.     }
  94.  
  95.     /* No, this line is longer than our buffer. */
  96.     (void)fprintf(stderr, "%s: Line %d (%.40s...) too long\n",
  97.         MyName, count, l);
  98.     fatals++;
  99.  
  100.     /* Trash the rest of the long line. */
  101.     while (fgets(l, len, stdin) != NULL && strchr(l, '\n') == NULL)
  102.         continue;
  103.  
  104.     /* Go back and get the nest (just ignore the long one). */
  105.     }
  106. }
  107.  
  108.  
  109. /*
  110. **  Remember a file name; this is pretty trivial (only fancy bit is not
  111. **  malloc'ing mem for short names).
  112. */
  113. STATIC dnode *
  114. build_node(prev, dir, name)
  115.     dnode        *prev;
  116.     chr            *dir;
  117.     chr            *name;
  118. {
  119.     register dnode    *n;
  120.     register int    i;
  121.  
  122.     n = NEW(dnode, 1);
  123.     if (prev)
  124.         prev->next = n;
  125.     n->next = NULL;
  126.     n->dir = dir;
  127.  
  128.     i = strlen(name);
  129.     if (i >= SHORT_NAME) {
  130.     n->longname = COPY(name);
  131.     }
  132.     else {
  133.     n->longname = NULL;
  134.     (void)strcpy(n->shortname, name);
  135.     }
  136.     return n;
  137. }
  138.  
  139.  
  140. /*
  141. **  Read lines from stdin (including the first that may have been there
  142. **  from our last time in) until we reach EOF, or until we get a line that
  143. **  names a file not in the same directory as the previous lot remember
  144. **  the file names in the directory we're examining, and count them
  145. */
  146. STATIC dnode *
  147. build_dir(ip)
  148.     int        *ip;
  149. {
  150.     static chr    line[MAX_LINE_SIZE];
  151.     register dnode    *start;
  152.     register dnode    *n;
  153.     register int    dlen;
  154.     register chr    *p;
  155.     register chr    *dir;
  156.  
  157.     *ip = 0;
  158.     if (line[0] == '\0' && !get_line(line, (int)sizeof line))
  159.     return NULL;
  160.  
  161.     /* Build node. */
  162.     p = (chr *)strrchr(line, '/');
  163.     if (p != NULL) {
  164.     *p++ = '\0';
  165.     dlen = strlen(line);
  166.     dir = COPY(line);
  167.     }
  168.     else {
  169.     dir = NULL;
  170.     dlen = -1;
  171.     p = line;
  172.     }
  173.     n = start = build_node((dnode *)NULL, dir, p);
  174.     *ip = 1;
  175.  
  176.     while (get_line(line, (int)sizeof line)) {
  177.     if ((dlen < 0 && strchr(line, '/'))
  178.      || (dlen >= 0 && (line[dlen] != '/'
  179.                   || strchr(line + dlen + 1, '/') != NULL
  180.                   || strncmp(dir, line, dlen))))
  181.         return start;
  182.  
  183.     n = build_node(n, dir, line + dlen + 1);
  184.     (*ip)++;
  185.     }
  186.     line[0] = '\0';
  187.     return start;
  188. }
  189.  
  190.  
  191. /*
  192. **  Sorting predicate for qsort to put nodes in alphabetical order.
  193. */
  194. STATIC int
  195. comp(a, b)
  196.     POINTER a;
  197.     POINTER b;
  198. {
  199.     dnode *l1, *l2;
  200.  
  201.     l1 = *CAST(dnode**, a);
  202.     l2 = *CAST(dnode**, b);
  203.     return strcmp(NODENAME(l1), NODENAME(l2));
  204. }
  205.  
  206.  
  207. /*
  208. **  Find a node in the list.
  209. */
  210. STATIC dnode *
  211. inlist(list, num, name)
  212.     register dnode    **list;
  213.     int            num;
  214.     char        *name;
  215. {
  216.     register dnode    **top;
  217.     register dnode    **cur;
  218.     register int    i;
  219.  
  220.     for (top = list + num - 1; top >= list; ) {
  221.     cur = list + (top - list) / 2;
  222.  
  223.     i = strcmp(name, NODENAME(*cur));
  224.     if (i == 0)
  225.         return *cur;
  226.  
  227.     if (i < 0)
  228.         top = cur - 1;
  229.     else
  230.         list = cur + 1;
  231.     }
  232.     return NULL;
  233. }
  234.  
  235.  
  236. /*
  237. **  Free a list of nodes.
  238. */
  239. STATIC void
  240. freelist(list)
  241.     register dnode *list;
  242. {
  243.     register dnode *l;
  244.  
  245.     while ((l = list) != NULL) {
  246.     list = l->next;
  247.     if (l->longname)
  248.         DISPOSE(l->longname);
  249.     DISPOSE(l);
  250.     }
  251. }
  252.  
  253.  
  254. STATIC void
  255. unlink_node(n)
  256.     dnode        *n;
  257. {
  258.     register chr    *p;
  259.     struct stat        sb;
  260.     int            oerrno;
  261.  
  262.     p = NODENAME(n);
  263.     if (prefix_len != 0) {
  264.     (void)strcpy(prefix_dir + prefix_len, p);
  265.     p = prefix_dir;
  266.     }
  267.  
  268.     if (AmRoot) {
  269.     if (stat(p, &sb) < 0) {
  270.         if (errno != ENOENT) {
  271.         oerrno = errno;
  272.         (void)fprintf(stderr, "%s: stat ", MyName);
  273.         if (*p != '/')
  274.             (void)fprintf(stderr, "in %s: ", cur_dir);
  275.         (void)fflush(stderr);
  276.         errno = oerrno;
  277.         perror(p);
  278.         fatals++;
  279.         }
  280.         return;
  281.     }
  282.     if (S_ISDIR(sb.st_mode)) {
  283.         (void)fprintf(stderr, "%s: Directory ", MyName);
  284.         if (*p != '/')
  285.         (void)fprintf(stderr, "in %s: ", cur_dir);
  286.         (void)fprintf(stderr, "\"%s\"\n", p);
  287.         fatals++;
  288.         return;
  289.     }
  290.     }
  291.  
  292.     if (Debugging) {
  293.     if (*p == '/')
  294.         (void)printf("%s\n", p);
  295.     else
  296.         (void)printf("%s / %s\n", cur_dir, p);
  297.     return;
  298.     }
  299.  
  300.     if (unlink(p) < 0) {
  301.     if (errno != ENOENT) {
  302.         oerrno = errno;
  303.         (void)fprintf(stderr, "%s: unlink ", MyName);
  304.         if (*p != '/')
  305.         (void)fprintf(stderr, "in %s: ", cur_dir);
  306.         (void)fflush(stderr);
  307.         errno = oerrno;
  308.         perror(p);
  309.         fatals++;
  310.     }
  311.     }
  312. }
  313.  
  314.  
  315. STATIC void
  316. copynsegs(from, to, n)
  317.     register chr    *from;
  318.     register chr    *to;
  319.     register int    n;
  320. {
  321.     register chr    c;
  322.  
  323.     while ((*to++ = c = *from++) != '\0')
  324.     if (c == '/' && --n <= 0)
  325.         break;
  326.  
  327.     if (c == '/')
  328.     *--to = '\0';
  329. }
  330.  
  331.  
  332. STATIC int
  333. slashcount(p)
  334.     register chr    *p;
  335. {
  336.     register int    i;
  337.  
  338.     for (i = 0; *p; )
  339.     if (*p++ == '/')
  340.         i++;
  341.     return i;
  342. }
  343.  
  344.  
  345. /*
  346. **  Set our environment (process working cirectory, and global vars) to
  347. **  reflect a change to directory 'd' (relative to base_dir if 'd' is not
  348. **  an absolute path).   We're likely to want to do different things
  349. **  depending on the amount of work to do in 'd' - that's given by 'num'.
  350. **  Return FALSE if the directory can be determined not to exist.
  351. */
  352. STATIC BOOL
  353. setup_dir(d, num)
  354.     register chr    *d;
  355.     int            num;
  356. {
  357.     register chr    *p;
  358.     register chr    *q;
  359.     register chr    *abs;
  360.     register int    bsegs;
  361.     register int    oerrno;
  362.     chr            string[MAX_DIR_LEN];
  363.  
  364.     bsegs = slashcount(base_dir);
  365.     if (d == NULL)
  366.     abs = base_dir;
  367.     else if (*d == '/')
  368.     abs = d;
  369.     else if (*d == '\0')
  370.     abs = (chr *)"/";
  371.     else {
  372.     while (d[0] == '.' && d[1] == '/')
  373.         for (d += 2; *d == '/'; )
  374.             d++;
  375.     while (bsegs > 0 && d[0] == '.' && d[1] == '.' && d[2] == '/')
  376.         for (bsegs--, d += 3; *d == '/'; )
  377.         d++;
  378.     if (bsegs <= 0)
  379.         err_exit("Can't handle that many ..'s in path");
  380.     abs = string;
  381.     copynsegs(base_dir, abs, bsegs + 1);
  382.     (void)strcat(abs, "/");
  383.     (void)strcat(abs, d);
  384.     }
  385.  
  386.     /* Now "abs" is the full path name of the directory we want to
  387.      * be at, and "cur_dir" is where we presently are. */
  388.     for (p = abs, q = cur_dir; *p == *q; ) {
  389.     /* If we've reached the end, this is easy, we want to be in
  390.      * the same place as we were (which is probably really some
  391.      * kind of error, it shouldn't happen). */
  392.     if (*p == '\0')
  393.         return TRUE;
  394.     p++;
  395.     q++;
  396.     }
  397.  
  398.     prefix_len = 0;
  399.     if (*p == 0 && *q == '/') {
  400.     /* Want to go back up the tree, it might be faster to chdir(abs)
  401.      * or chdir(../../..).  But, since the this case happens rarely if
  402.      * the user cares about speed (sorted input will usually mean that
  403.      * we don't simply want to go back up the tree) it's not worth the
  404.      * bother. */
  405.     if (cdval == 0 || num < cdval) {
  406.         /* Except if we have just a couple of files in this directory
  407.          * to deal with, in which case we'll just use their absolute
  408.          * path names. */
  409.         (void)strcpy(prefix_dir, abs);
  410.         prefix_len = p - abs;
  411.         return TRUE;
  412.     }
  413.  
  414.     if (chdir(abs) < 0) {
  415.         oerrno = errno;
  416.         (void)fprintf(stderr, "%s: chdir to ", MyName);
  417.         (void)fflush(stderr);
  418.         errno = oerrno;
  419.         perror(abs);
  420.         /* If we fail here, something is badly broken, since we're
  421.          * supposedly further down the tree. */
  422.         err_exit("Chdir failed");
  423.     }
  424.     *q = '\0';
  425.     return TRUE;
  426.     }
  427.  
  428.     if (*q == '\0' && *p == '/') {
  429.     /* Want to change into a sub-dir of where we were; easy. */
  430.     p++;
  431.     if (cdval == 0 || num < cdval) {
  432.         (void)strcpy(prefix_dir, p);
  433.         prefix_len = strlen(p);
  434.         return TRUE;
  435.     }
  436.  
  437.     if (chdir(p) < 0) {
  438.         oerrno = errno;
  439.         (void)fprintf(stderr, "%s: chdir from %s to ", MyName,
  440.             cur_dir);
  441.         (void)fflush(stderr);
  442.         errno = oerrno;
  443.         perror(p);
  444.         if (oerrno == ENOENT)
  445.         return FALSE;
  446.         err_exit("Chdir failed");
  447.     }
  448.     (void)strcpy(cur_dir, abs);
  449.     return TRUE;
  450.     }
  451.  
  452.     /* If its possible, (promised we have a pure tree), see if its
  453.      * worth going up the tree with ".." then down again, or if
  454.      * its better to simply start again at the start. */
  455.     if (dotdot) {
  456.     bsegs = slashcount(q);
  457.     /* 1 default "dotdot" here can be 0, 1, 2, or 3, 1 seems
  458.      * frationally faster than 2, bigger values would require
  459.      * extending the "../../../" string, but are very unlikely
  460.      * to be helpful; '0' is the same as not using -u. */
  461.     if (bsegs <= dotdot) {
  462.         /* Looks like its probably worth using "..". */
  463.         while (p > abs && *--p != '/')
  464.         continue;
  465.         p++;
  466.         (void)strcpy(prefix_dir, DotDot + 9 - bsegs * 3);
  467.         (void)strcpy(prefix_dir + (bsegs + 1) * 3, p);
  468.  
  469.         if (cdval == 0 || num < cdval) {
  470.         prefix_len = strlen(prefix_dir);
  471.         return TRUE;
  472.         }
  473.  
  474.         if (chdir(prefix_dir) < 0) {
  475.         oerrno = errno;
  476.         (void)fprintf(stderr, "%s: chdir from %s to ",
  477.             MyName, cur_dir);
  478.         (void)fflush(stderr);
  479.         errno = oerrno;
  480.         perror(prefix_dir);
  481.         if (oerrno == ENOENT)
  482.             return FALSE;
  483.         err_exit("Chdir failed");
  484.         }
  485.  
  486.         /* Now patch up curdir to reflect where we are. */
  487.         while (q > cur_dir && *--q != '/')
  488.         continue;
  489.         (void)strcpy(q + 1, p);
  490.         return TRUE;
  491.     }
  492.     }
  493.  
  494.     /* Simply use the absolute path. */
  495.     if (cdval == 0 || num < cdval) {
  496.     (void)strcpy(prefix_dir, abs);
  497.     prefix_len = strlen(abs);
  498.     return TRUE;
  499.     }
  500.     if (chdir(abs) < 0) {
  501.     oerrno = errno;
  502.     (void)fprintf(stderr, "%s: chdir to ", MyName);
  503.     (void)fflush(stderr);
  504.     errno = oerrno;
  505.     perror(abs);
  506.     if (oerrno == ENOENT)
  507.         return FALSE;
  508.     err_exit("Chdir failed");
  509.     }
  510.  
  511.     (void)strcpy(cur_dir, abs);
  512.     return TRUE;
  513. }
  514.  
  515.  
  516. STATIC void
  517. unlink_dir(list, num)
  518.     register dnode    *list;
  519.     register int    num;
  520. {
  521.     static dnode    **dptrs;
  522.     static int        ndp;
  523.     register dnode    *l;
  524.     register dnode    **pl;
  525.     register DIR    *dfd;
  526.     register DIRENTRY    *d;
  527.     register BOOL    sorted;
  528.     struct stat        sb;
  529.  
  530.     if (!setup_dir(list->dir, num)) {
  531.     /* The directory doesn't exist, no point attempting to
  532.      * delete anything, just forget it all. */
  533.     if (list->dir)
  534.         DISPOSE(list->dir);
  535.     freelist(list);
  536.     return;
  537.     }
  538.  
  539.     if (list->dir)
  540.     DISPOSE(list->dir);
  541.     if (sortdirs == 0 || num < sortdirs) {
  542.     if (prefix_len != 0) {
  543.         prefix_dir[prefix_len++] = '/';
  544.         prefix_dir[prefix_len] = '\0';
  545.     }
  546.     /*  Easier to just unlink the files than worry about the
  547.      *  order we unlink them in. */
  548.     while ((l = list) != NULL) {
  549.         unlink_node(l);
  550.         list = l->next;
  551.         if (l->longname)
  552.         DISPOSE(l->longname);
  553.         DISPOSE(l);
  554.     }
  555.     return;
  556.     }
  557.  
  558.     if (ndp == 0) {
  559.     ndp = num;
  560.     dptrs = NEW(dnode*, ndp);
  561.     }
  562.     else if (num > ndp) {
  563.     ndp = num + 16;
  564.     RENEW(dptrs, dnode*, ndp);
  565.     }
  566.  
  567.     if ((pl = dptrs) == NULL)
  568.     err_exit("Out of mem in unlink_dir");
  569.  
  570.     for (sorted = TRUE, *pl = list, l = list->next; l; l = l->next) {
  571.     if (sorted && strcmp(NODENAME(*pl), NODENAME(l)) > 0)
  572.         sorted = FALSE;
  573.     *++pl = l;
  574.     }
  575.  
  576.     if (!sorted)
  577.     qsort((char *)dptrs, num, sizeof (dnode *), comp);
  578.  
  579.     if (prefix_len == 0) {
  580.     if ((dfd = opendir(".")) == NULL) {
  581.         (void)fprintf(stderr, "Can't open \".\" in directory \"%s\"\n",
  582.             cur_dir);
  583.         fatals++;
  584.         freelist(list);
  585.         return;
  586.     }
  587.     }
  588.     else {
  589.     if ((dfd = opendir(prefix_dir)) == NULL) {
  590.         if (prefix_dir[0] == '/')
  591.         (void)fprintf(stderr, "Can't open directory \"%s\"\n",
  592.             prefix_dir);
  593.         else
  594.         (void)fprintf(stderr, "Can't open directory \"%s\" in \"%s\"\n",
  595.             prefix_dir, cur_dir);
  596.         if (stat(prefix_dir, &sb) >= 0 || errno != ENOENT)
  597.             fatals++;
  598.         freelist(list);
  599.         return;
  600.     }
  601.     }
  602.  
  603.     if (prefix_len != 0) {
  604.     prefix_dir[prefix_len++] = '/';
  605.     prefix_dir[prefix_len] = '\0';
  606.     }
  607.  
  608.     while ((d = readdir(dfd)) != NULL)
  609.     if ((l = inlist(dptrs, num, d->d_name)) != NULL)
  610.         unlink_node(l);
  611.     (void)closedir(dfd);
  612.     freelist(list);
  613. }
  614.  
  615.  
  616. STATIC BOOL
  617. bad_path(p)
  618.     register char    *p;
  619. {
  620.     while (*p) {
  621.     if (p[0] == '.' && (p[1] == '/' || p[1] == '.' && p[2] == '/'))
  622.         return TRUE;
  623.     while (*p && *p != '/')
  624.         p++;
  625.     if (p[0] == '/' && p[1] == '/')
  626.         return TRUE;
  627.     if (*p == '/')
  628.         p++;
  629.     }
  630.     return FALSE;
  631. }
  632.  
  633.  
  634. int
  635. main(ac, av)
  636.     int        ac;
  637.     char    *av[];
  638. {
  639.     register dnode    *list;
  640.     register char    *p;
  641.     register int    oerrno;
  642.     int            count;
  643.     BOOL        empty_error;
  644.  
  645.     MyName = av[0];
  646.     if ((p = strrchr(MyName, '/')) != NULL)
  647.     MyName = p + 1;
  648.     ONALLLOCFAIL(myexit);
  649.     AmRoot = geteuid() == 0;
  650.     empty_error = FALSE;
  651.  
  652.     while (ac > 2) {
  653.     if (*(p = av[1]) != '-')
  654.         break;
  655.  
  656.     while (*++p) {
  657.         switch (*p) {
  658.         default:
  659.         (void)fprintf(stderr, "Usage: %s [ -u -s ] base_dir\n", MyName);
  660.         exit(1);
  661.         case 'd':
  662.         Debugging = TRUE;
  663.         continue;
  664.         case 'u':
  665.         dotdot = 1;
  666.         if (!isdigit(p[1]))
  667.             continue;
  668.         dotdot = atoi(p + 1);
  669.         if (dotdot >= strlen(DotDot)/(SIZE_T)3)
  670.             dotdot = strlen(DotDot)/(SIZE_T)3 - 1;
  671.         break;
  672.         case 's':
  673.         sortdirs = 5;
  674.         if (!isdigit(p[1]))
  675.             continue;
  676.         sortdirs = atoi(p + 1);
  677.         break;
  678.         case 'c':
  679.         cdval = 1;
  680.         if (!isdigit(p[1]))
  681.             continue;
  682.         cdval = atoi(p + 1);
  683.         break;
  684.         case 'e':
  685.         empty_error = TRUE;
  686.         continue;
  687.         case 'a':
  688.         case 'r':
  689.         continue;
  690.         }
  691.         break;
  692.     }
  693.     ac--;
  694.     av++;
  695.     }
  696.  
  697.     if (ac != 2) {
  698.     (void)fprintf(stderr, "Usage: %s base_dir\n", MyName);
  699.     exit(1);
  700.     }
  701.  
  702.     p = av[1];
  703.     if (*p != '/' || bad_path(p) || strlen(p) >= (SIZE_T)MAX_DIR_LEN) {
  704.     (void)fprintf(stderr, "%s: Bad base path: %s\n", MyName, p);
  705.     exit(1);
  706.     }
  707.  
  708.     (void)strcpy(base_dir, p);
  709.     (void)strcpy(cur_dir, p);
  710.  
  711.     if (chdir(cur_dir) < 0) {
  712.     oerrno = errno;
  713.     (void)fprintf(stderr, "%s: chdir to base path ", MyName);
  714.     (void)fflush(stderr);
  715.     errno = oerrno;
  716.     perror(cur_dir);
  717.     exit(1);
  718.     }
  719.  
  720.     while ((list = build_dir(&count)) != NULL) {
  721.     empty_error = FALSE;
  722.     unlink_dir(list, count);
  723.     }
  724.     if (fatals || empty_error)
  725.     exit(1);
  726.  
  727.     exit(0);
  728.     /* NOTREACHED */
  729. }
  730.